package org.ihelpuoo.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;

import org.ihelpuoo.I;
import org.ihelpuoo.io.CharInputStream;
import org.ihelpuoo.io.TextTokenReader;
import org.ihelpuoo.lang.StringUtils;
import org.ihelpuoo.parser.segment.CharSegment;
import org.ihelpuoo.parser.segment.Segment;

public class Properties implements Map<String, String>
{
	public Properties(InputStream ins, String encoding) throws IOException
	{
		maps = new HashMap<String, String>();
		this.encoding = encoding;
		load(ins);
	}

	public Properties(InputStream ins) throws IOException
	{
		this(ins,null);
	}

	public Properties()
	{
		maps = new HashMap<String, String>();
		this.encoding = null;
	}

	public Properties(CharSequence cs) throws IOException
	{
		this(new CharInputStream(cs));
	}

	public Properties(Properties p)
	{
		this();
		maps.putAll(p.maps);
	}

	public Properties(java.util.Properties p)
	{
		maps = new HashMap<String, String>();
		Enumeration en = p.keys();
		while (en.hasMoreElements())
		{
			String key = (String)en.nextElement();
			String v = (String)p.get(key);
			maps.put(key,v);
		}
	}

	protected Map<String, String>	maps;
	protected String				encoding;

	public void setEncoding(String encoding)
	{
		this.encoding = encoding;
	}

	public synchronized void load(InputStream ins) throws IOException
	{
		TextTokenReader tr = new TextTokenReader((null == encoding ? new InputStreamReader(ins)
				: new InputStreamReader(ins,encoding)));
		this.clear();
		String s;
		while (null != (s = tr.readLineIgnoreBlank()))
		{
			if (s.startsWith("#"))
				continue;
			int pos;
			char c = '0';
			for (pos = 0; pos < s.length(); pos++)
			{
				c = s.charAt(pos);
				if (c == '=' || c == ':')
					break;
			}
			if (c == '=')
			{
				String name = s.substring(0,pos);
				maps.put(name,s.substring(pos + 1));
			}
			else if (c == ':')
			{
				String name = s.substring(0,pos);
				StringBuffer sb = new StringBuffer();
				sb.append(s.substring(pos + 1));
				String ss;
				while (null != (ss = tr.readLine()))
				{
					if (ss.startsWith("#"))
						break;
					sb.append(I.NEW_LINE + ss);
				}
				maps.put(name,sb.toString());
				if (null == ss)
					return;
			}
			else
			{
				maps.put(s,null);
			}
		}
	}

	public synchronized String setProperty(String key, String value)
	{
		return maps.put(key,value);
	}

	public String getString(String key)
	{
		Object v = (String)maps.get(key);
		if (null == v)
			return null;
		return v.toString();
	}

	public String getString(String key, String defaultValue)
	{
		String v = getString(key);
		return (null == v ? defaultValue : v);
	}

	public String getStringTrimed(String key)
	{
		return StringUtils.trim(this.getString(key));
	}

	public String getStringTrimed(String key, String defaultValue)
	{
		return StringUtils.trim(this.getString(key,defaultValue));
	}

	public int getInt(String key)
	{
		Object obj = get(key);
		if (null != obj)
			if (obj instanceof Integer)
				return ((Integer)obj).intValue();
			else
				return Integer.parseInt(obj.toString());
		return -1;
	}

	public float getFloat(String key)
	{
		Object obj = get(key);
		if (null != obj)
			if (obj instanceof Float)
				return ((Float)obj).floatValue();
			else
				return Float.parseFloat(obj.toString());
		return -1;
	}

	public boolean getBoolean(String key)
	{
		Object obj = get(key);
		if (null != obj)
			if (obj instanceof Float)
				return ((Boolean)obj).booleanValue();
			else
				return Boolean.parseBoolean(obj.toString());
		return false;
	}

	public long getLong(String key)
	{
		Object obj = get(key);
		if (null != obj)
			if (obj instanceof Float)
				return ((Long)obj).longValue();
			else
				return Long.parseLong(obj.toString());
		return -1L;
	}

	public double getDouble(String key)
	{
		Object obj = get(key);
		if (null != obj)
			if (obj instanceof Float)
				return ((Double)obj).doubleValue();
			else
				return Double.parseDouble(obj.toString());
		return -1;
	}

	public synchronized void clear()
	{
		maps.clear();
	}

	public boolean containsKey(Object key)
	{
		return maps.containsKey(key);
	}

	public boolean containsValue(Object value)
	{
		return maps.containsValue(value);
	}

	public Set<Entry<String, String>> entrySet()
	{
		return maps.entrySet();
	}

	@Override
	public boolean equals(Object o)
	{
		return maps.equals(o);
	}

	@Override
	public int hashCode()
	{
		return maps.hashCode();
	}

	public boolean isEmpty()
	{
		return maps.isEmpty();
	}

	public Set<String> keySet()
	{
		return maps.keySet();
	}

	public synchronized String put(String key, String value)
	{
		return maps.put(key,value);
	}

	@SuppressWarnings("unchecked")
	public synchronized void putAll(Map t)
	{
		maps.putAll(t);
	}

	public synchronized String remove(Object key)
	{
		return maps.remove(key);
	}

	public int size()
	{
		return maps.size();
	}

	public Collection<String> values()
	{
		return maps.values();
	}

	public String get(Object key)
	{
		return maps.get(key);
	}

	@Override
	public Object clone() throws CloneNotSupportedException
	{
		return new Properties(this);
	}

	public static void updatePropertiesByServletContext(Properties p, ServletContext context)
	{
		Iterator<String> it = p.keySet().iterator();
		while (it.hasNext())
		{
			String k = it.next();
			p.setProperty(k,formatValue(p.getString(k),context));
		}
	}

	private static String formatValue(String v, ServletContext context)
	{
		Segment seg = new CharSegment(v);
		seg.set("appRoot",context.getRealPath("/"));
		return seg.toString();
	}
}
